/*
 * Copyright 2011 Carnegie Mellon University
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, 
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package edu.cmu.lti.ritesdk;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * Responsible for reading/writing RITE data from file system. 
 * 
 * TODO: we need a reader that parses system output file.
 * 
 * @author Hideki Shima
 *
 */
public class FileUtils {
  
  /**
   * Deserializes input data from xml.
   * 
   * @param file
   * @return dataset
   * @throws Exception
   */
  public static DataSet readRiteXml( File file ) throws Exception {
    try {
      return readRiteXml( new FileInputStream(file) );
    } catch (Exception e) {
      System.out.println("ERROR: There is a problem parsing: "+file.getAbsolutePath());
      throw e;
    }
  }
  
  /**
   * Deserializes input data from xml.
   * 
   * @param is
   * @return dataset
   * @throws Exception
   */
  public static DataSet readRiteXml( InputStream is ) throws Exception {
    DataSet d = new DataSet();
    
    Document doc = null;
    DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = dbfactory.newDocumentBuilder();
    doc = builder.parse(is);
    Element root = doc.getDocumentElement();
    NodeList list = root.getElementsByTagName("pair");
    for (int i = 0; i < list.getLength(); i++) {
      Element element = (Element) list.item(i);
      String id = cleanup(element.getAttribute("id"));
      String label = cleanup(element.getAttribute("label"));
      String t1 = ((Element)element.getElementsByTagName("t1").item(0)).getTextContent();
      String t2 = ((Element)element.getElementsByTagName("t2").item(0)).getTextContent();
      TextPair t = new TextPair(t1, t2, id, label);
      d.put(id,t);
    }
    return d;
  }
  
  /**
   * Writes analyzed result (system output) to a file.
   * 
   * @param dataset
   * @param outputFile
   * @throws Exception
   */
  public static void writeRiteResultToFile( AnalyzedDataSet dataset, 
          File outputFile ) throws Exception {

    File outputDir = outputFile.getParentFile();
    if (!outputDir.exists()) {
      outputDir.mkdirs();
    }

    FileOutputStream fos = new FileOutputStream(outputFile);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    BufferedWriter bw = new BufferedWriter(osw);
    for ( AnalyzedTextPair pair : dataset.values() ) {
      bw.write( pair.getTextPair().getId()+" "+
                pair.getSystemLabel()+" "+
                pair.getConfidence()+"\n" ); 
    }
    bw.close();
    osw.close();
    fos.close();
  }
  
  /**
   * Writes a dataset into xml.
   * 
   * <pre>
   * <dataset type="bc">
   *   <pair id="1" label="Y">
   *     <t1>text</t1>
   *     <t2>hypothesis</t2>
   *   </pair>
   *   : : : 
   * </dataset>
   * </pre>
   * 
   * @param dataset
   * @param outputFile
   * @throws Exception
   */
  public static void writeRiteDataToFile( DataSet dataset, 
          File outputFile ) throws Exception {
    
    StringBuilder sb = new StringBuilder();
    sb.append("<dataset>\n");
    for ( TextPair pair : dataset.values() ) {
      sb.append("  <pair id=\""+pair.getId()+"\" label=\""+pair.getGoldStandardLabel()+"\">\n");
      sb.append("    <t1>"+pair.getT1()+"</t1>\n");
      sb.append("    <t2>"+pair.getT2()+"</t2>\n");
      sb.append("  </pair>\n");
    }
    sb.append("</dataset>\n");
    
    writeStringToFile(sb.toString(), outputFile);
  }
  
  /**
   * General method that writes a String content to a file. 
   * 
   * @param content
   * @param outputFile
   * @throws Exception
   */
  public static void writeStringToFile( String content,
          File outputFile ) throws Exception {
    
    File outputDir = outputFile.getParentFile();
    if (outputDir!=null && !outputDir.exists()) {
      outputDir.mkdirs();
    }
    FileOutputStream fos = new FileOutputStream(outputFile);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    BufferedWriter bw = new BufferedWriter(osw);
    bw.write( content ); 
    bw.close();
    osw.close();
    fos.close();
  }
  
  /**
   * General method that writes a String content to a file. 
   * 
   * @param content
   * @param outputFile
   * @throws Exception
   */
  public static void appendStringToFile( String content,
          File outputFile ) throws Exception {
    
    File outputDir = outputFile.getParentFile();
    if (outputDir!=null && !outputDir.exists()) {
      outputDir.mkdirs();
    }
    FileOutputStream fos = new FileOutputStream(outputFile, true);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    BufferedWriter bw = new BufferedWriter(osw);
    bw.write( content ); 
    bw.close();
    osw.close();
    fos.close();
  }

  /**
   * General method that reads a file content to a String.
   * 
   * @param outputFile
   * @return string
   * @throws Exception
   */
  public static String readStringFromFile( File outputFile ) throws Exception {
    StringBuilder sb = new StringBuilder();
    FileInputStream fis = new FileInputStream(outputFile);
    InputStreamReader isr = new InputStreamReader(fis);
    BufferedReader br = new BufferedReader(isr);
    String line = null;
    while ( (line=br.readLine())!=null ) {
      sb.append(line+"\n");
    }
    br.close();
    isr.close();
    fis.close();
    return sb.toString();
  }

  /**
   * Deserializes system output file.
   * 
   * @param file
   * @param data
   * @return analyzed dataset
   * @throws Exception
   */
  public static AnalyzedDataSet readRunResult( File file, 
          DataSet data ) throws Exception {
    AnalyzedDataSet dataset = new AnalyzedDataSet();
    FileInputStream fis = new FileInputStream(file);
    InputStreamReader isr = new InputStreamReader(fis);
    BufferedReader br = new BufferedReader(isr);
    String line = null;
    while ((line=br.readLine())!=null) {
      String[] items = line.split("[ \t]");
      if ( items.length <= 1 ) continue;
      try {
        String id = cleanup(items[0]);
        TextPair pair = data.get(id);
        String systemLabel = cleanup(items[1]);
        Double confidence = null;
        if ( items.length >= 3 ) {
          String confText = cleanup(items[2]);
          try {
            confidence = Double.parseDouble( confText );
          } catch (NumberFormatException e) {
            if (confidence!=null) {
              System.out.println("ERROR: [ID = "+id+"] Confidence score \""+confidence+"\" is not a number; setting 1 instead.");
            }
          }
          if (confidence!=null && (confidence<0 || confidence>1)) {
            System.out.println("ERROR [ID = "+id+"] Confidence score \""+confidence+"\" is not a real value in [0,1]; setting 1 instead.");
          }
        }
        if (confidence==null) confidence = 1.0D;
        AnalyzedTextPair analyzedPair = new AnalyzedTextPair(pair, systemLabel, confidence);
        dataset.put(id, analyzedPair);
      } catch (Exception e) {}
    }
    // Fill in run data that are missing
    for ( String id : data.keySet() ) {
      AnalyzedTextPair analyzedPair = dataset.get(id);
      if (analyzedPair==null) {
        dataset.put(id, new AnalyzedTextPair(data.get(id), "", 0d));
      }
    }
    br.close();
    isr.close();
    fis.close();
    return dataset;
  }
  
  /**
   * Removes illegal BOM characters in the beginning of a text file.
   * @param s
   * @return clean string
   */
  private static String cleanup(String s) {
    return s.replaceAll("[^A-Za-z0-9. _-]", "");
  }
  
}
